A/D変換の動作確認(1)
A/D変換の動作を実感してみよう!
A/D変換器のポートを選ぶ
A/D変換器の入力ポートですが、PIC16F877は、(RA0/AN0),(RA1/AN1), (RA2/AN2),(RA3/AN3),(RA5/AN4),(RE0/AN5) , (RE1/AN6) , (RE2/AN7) の全8ポートあります。まずは簡単のため、1つのA/D変換ポートとして、(RA0/AN0)を使いましょう。またリファレンス電圧(VREF+,VREF-)は未使用とします。ここで、「A/D変換の制御法」の表3.A/D ポートの設定表にて、A/Dポートの設定を確認します。PCFGx=「1110」のとき、(RA0/AN0)がA/D変換入力ポートとなり、他のポートはディジタル入出力ポートになることが分かります。
さらに、「A/D変換の制御法」の表1.ADCON0レジスタの機能説明表にて、アナログチャネル指定ビットですが、CHSx=「000」とすることで、CH0(RA0)の指定になります。
A/D結果レジスタの右/左詰めについて
ADRESH,ADRESL レジスタのペアは、A/D 変換の終了時に10 ビットのA/D 結果がロードされるロケーションです。(「A/D変換の制御法」の図8.A/D 結果の右/左詰めを参照)このレジスタのペアは16 ビット幅です。したがって、10ビットのデータが16ビットの幅に入るため、残りの6ビットは、空ビットになります。この10ビットを16ビット幅のどちら側に詰めるべきかを決める必要があります。今回は、他のポートに確認用のLEDを接続し、A/D変換の結果をLEDに点灯させて簡単に確認できるようにします。LEDの接続ポートですが、私のターゲットボードでは、PortBとPortDが隣り合って並んでいますので、10ビットの結果を右詰めにしたいと思います。したがって、
ADRESH<1:0>→PortB<1:0>
ADRESL<7:0>→PortD<7:0>
という配置に出力させます。
すなわち、10ビットのMSBから順に<RB1,RB0,RD7,RD6,・・・,RD0>とします。
A/D変換のクロックの選択
「A/D変換の制御法」の表4.A/D変換用クロックの選択を参照してください。内部RC発振回路を使用するFrc指定の場合は、システムクロックには影響されず、2〜6μsの周波数になりますが、正確なクロック周波数ではなくなります。そこで、Fosc/2,Fosc/8,Fosc/32のどれかを選ぶことになります。システムクロックの周波数により、指定できる範囲が限られていますので注意が必要です。正しいA/D 変換のためには、必ずA/D 変換クロック(TAD)を、最小TAD
時間の1.6μs以上にする必要がありました。
20MHzのシステムクロックでは、Fosc/32を選択することになります。
よって、ADCS1=「1」,ADCS0=「0」と決まります。
ADCON0レジスタの決定
(「A/D変換の制御法」の図1. ADCON0レジスタの構成を参照)
ADCON0レジスタの内容の決定
ということで、ADCON0→B’10000001’ (81H)
ADCON1レジスタの決定
(「A/D変換の制御法」の図2. ADCON1レジスタの構成を参照)
ADCON1レジスタの内容の決定
ということで、ADCON1→B’10001110’ (8EH)
ハード側の接続について
A/D変換の結果を簡単に確認するためLEDを接続し点灯させます。LEDの接続ポートですが、私のターゲットボードでは、PortBとPortDが隣り合って並んでいますので、図1のように10ビットのMSBから順に<RB1,RB0,RD7,RD6,・・・,RD0>と接続します。A/D変換入力の端子ですが、簡単のため10kΩの可変抵抗を図1のように接続し、0〜5Vの可変電圧を入力できるようにしました。
接続先のインピーダンスは10kΩ以下にせよ!!
アナログソースの最大推奨インピーダンスは
10kΩですから、それより大きい抵抗値は好ましくありません。接続先のインピーダンスが大きいと、アクィジション時間が長くなるため、A/D変換動作に支障をきたします。逆に小さい抵抗値ですと、抵抗による消費電流が増えますので注意してください。
10kΩの可変抵抗器ですが、タップが中央の位置のときインピーダンスは最大の2.5kΩになります。すなわち、中点から見ると、ちょうど
5kΩ と 5kΩ の並列接続に見えます。
なお、逆にタップが端に位置したとき(+5V、もしくは0Vのとき)可変抵抗のインピーダンスは、最小の0Ωになります。
![]() |
図1.A/D変換の動作確認回路 |
アナログ入力端子ですが、テスタなどを接続してアナログの電圧値が分かるようにします。また、アナログ入力電圧が安定しない場合には、10kΩの可変抵抗に
0.1μF程度のセラミックコンデンサを接続するとよいでしょう。
LEDの接続ですが、ポートに負荷がかからないようにターゲットボードにはバッファ回路がついています。バッファ回路を省略する場合には、LEDに接続の200Ωをもう少し大きくする必要があります。各ポートは1ピン当たり、最大25mAまで電流を流せますが、入出力ピンの合計最大ドライブ電流が200mAを越えないようにする必要があります。全10ビット点灯すると、1ピン当たり200mA/10=20mAとなります。ただし、入出力ポート以外の消費電流があるので、余裕をみて10mAと設定します。LEDの降下電圧を約1.5Vすれば、抵抗は、(5V−1.5V)/10mA=350Ωですので、330Ω〜360Ω程度の抵抗を接続すればOKです。
A/D変換動作確認プログラム(1)
図2にA/D変換のフローチャートを示します。
プログラムの解説については、「プログラムの説明(1)」の項をご参照ください。
![]() |
図2.A/D変換プログラム(1) フローチャート |
(注)以下に示すプログラムには、ホームページ画面作成の都合上、空白として全角文字のスペースなどが挿入されています。したがって、下記プログラムリストをそのままコピーしてMPLABのソースファイルとされた場合には、エラーとなることがあります。
→ここをクリックして、下記のプログラムをダウンロードするようにしてください。
ファイル名:「adconv1.asm」 サイズ2.22kバイト
→ここをクリックして、下記のオブジェクトファイルをダウンロードするようにしてください。
ファイル名:「adconv1.hex」 サイズ165バイト
;*********************************************************** ; A/D変換動作チェックプログラム(1) ; A/D変換入力端子(CH0/RA0) ; A/D変換の結果はLED点灯により確認 ; RB1(MSB),RB0,RD7,RD6,RD5,RD4,RD3,RD2,RD1,RD0(LSB) ; の10ビット ; A/D 割り込みは使用せず、簡単のため GO ビットで判断 ;*********************************************************** |
|
LIST P=PIC16F877 INCLUDE P16F877.INC |
;(1)プロセッサの種別指定 ;(2)インクルードファイルの指定 |
;*********************************************************** ; 変数定義とレジスタ割付 ;*********************************************************** |
|
COUNT EQU 20H ORG 0 |
;(3)ループカウンタ ;(4)プログラムの開始番地の指定 |
;*********************************************************** ; 入出力ピン初期化 ;*********************************************************** |
|
BSF STATUS,RP0 MOVLW B'10001110' MOVWF ADCON1 CLRF TRISB CLRF TRISD BCF STATUS,RP0 MOVLW B'10000001' MOVWF ADCON0 |
;(5)Bank 1 へ切替 ;(6)RA0のみアナログ、結果右詰 ;(7)ADCON1レジスタの設定 ;(8)PortB 出力モードに設定 ; PortD 出力モードに設定 ;(9)Bank 0 へ戻す ; ;(10)Fosc/32,CH0 設定, A/D on ;(11)ADCON0レジスタの設定 |
;*********************************************************** ; メインプログラム ;*********************************************************** |
|
ADSTART CALL TIME BSF ADCON0,GO WAIT BTFSC ADCON0,GO GOTO WAIT MOVF ADRESH,W MOVWF PORTB BSF STATUS,RP0 MOVF ADRESL,W BCF STATUS,RP0 MOVWF PORTD GOTO ADSTART |
; ;(12)23.2μs 一定時間待つ ; (2TAD [2×1.6μs]+アクィジション時間[20μs]) ;(13)A/D スタート ;(14)A/D が完了すると GOが0になる ;(15)GO が 0 だったらループから抜ける ; GO が 1 だったら WAIT へループする ;(16)A/D データ上位(データは右詰) ;(17)PortBへ A/D 結果を出力 ;(18)Bank 1 へ切替 ;(19)A/D データ下位 ;(20)Bank 0 へ戻す ;(21)PortDへ A/D 結果を出力 ;(22)A/D 変換を繰り返す |
;*********************************************************** ;23μs遅延サブルーチン(20MHzクロック時) ;*********************************************************** |
|
TIME MOVLW 25H MOVWF COUNT NOP LOOP DECFSZ COUNT,F GOTO LOOP RETURN END |
; ;(23)1サイクル 25H = 37 ;(24)1サイクル ;(25)1サイクル 微調整ダミー ; ;(26)1×(37-1)+2 = 38サイクル ;(27)2×(37-1) = 72サイクル ;(28)2サイクル ; 合計 115サイクル×0.2μs = 23μs ;(29)プログラム・ソースの終わり |
【プログラムの説明(1)】
このプログラムについて、順を追って解説を加えておきましょう。
(1)プロセッサの種別指定
定義の仕方は「PROCESSOR」か「LIST」命令を使って設定します。ここで指定するプロセッサ名称は、パッケージの種類を示すサフィックス(最後の英記号の部分)は不要です。
PROCESSOR PIC16F877
または
LIST P=PIC16F877
(2)標準ヘッダーファイルのインクルード
標準ヘッダーファイルとは、各プロセッサが持っているSFR(Special
Function Register)をラベル(記号)で使える様にするため、ラベルとハードウェアの場所とを定義しているファイルです。標準ヘッダーファイルは 「プロセッサ名.INC」というファイル名で統一されて、MPLABのディレクトリに格納されています。従って、これのインクルード方法は下記のようにして行います。
一度、参考までに標準ヘッダーファイルの内容をエディタ等で見ておくことをお勧めします。
INCLUDE P16F877.INC
または
#INCLUDE P16F877.INC
(3)変数定義とレジスタ割付
レジスタファイルアドレスを指定するときに、アドレス数値を直接指定することもできますが、数値だけでは間違いも多く、プログラム自身も分かりにくくなってしまいます。そこで、EQU命令などを使ってラベルを設定し変数を定義します。レジスタファイルアドレスは、7ビットあるので、00〜7Fまで最大128個のレジスタが指定できますが、実際に物理的に実装されて汎用的に使用できるレジスタ数はデバイスによって異なっていますので注意が必要です。
PIC16F877の汎用レジスタのアドレスは、20H〜7FH
となっていますので、20H以降のアドレスに割り付けます。
(4)プログラムの開始番地の指定
「ORG」はプログラムの開始番地を指定する擬似命令で、ORG以下の実際のプログラム命令が格納されるプログラムメモリ内の位置(アドレス)を指定します。
ORG 0 ;0番地から格納することを示します。
コンピュータは一般に電源投入時やリセットをすると必ず0番地からスタートするようになっているので、0番地には必ず命令があることが必要です。
(5)Bank 1 へ切替
PICには各種の動作モードを設定するための Special
Register と呼ばれるものが用意されています。PICを動作させるためには、まずこのSpecial
Registerの設定から始めます。そしてそれらは全て、Register
File と呼ぶメモリとして用意されています。その
Register Fileは Bank0, Bank1, Bank2, Bank3
とよばれるアドレス空間をもっているため、多少アクセスの仕方が面倒です。つまりRESET後の通常はBank0となっているので、Bank1側のレジスタにアクセスするときはBankの切替えをしてからとなります。またBank0とBank1に同じ物があるときにはどちらでも同じ様に使えます。
Bank1へ切り替える方法ですが、「STATUS」レジスタにある2ビットのRP0、RP1を変えてBankを指定します。デフォルトは、Bank0です。表1にBankとRP1,RP0ビットとの関係を示します。Bank1へ切り替えるためには、RP0ビットを「1」にします。(RP1はデフォルトで「0」なので変える必要はない。)
BSF STATUS,RP0
「STATUS」レジスタのRP0ビットを「1」にする。
Bank | RP1 | RP0 |
0 | 0 | 0 |
1 | 0 | 1 |
2 | 1 | 0 |
3 | 1 | 1 |
(6)RA0のみアナログ、結果右詰
A/D変換器の入力ポートとして、(RA0/AN0)を使います。またリファレンス電圧(VREF+,VREF-)は未使用とします。ここで、「A/D変換の制御法」の表3.A/D ポートの設定表にて、A/Dポートの設定を確認します。PCFGx=「1110」のとき、(RA0/AN0)がA/D変換入力ポートとなり、他のポートはディジタル入出力ポートになることが分かります。A/D 結果フォーマットは右詰に設定します。
bit7: A/D 結果フォーマットセレクト:右詰=「1」
bit6: 機能なし(とりあえず「0」とでもしておく)
bit5: 機能なし(とりあえず「0」とでもしておく)
bit4: 機能なし(とりあえず「0」とでもしておく)
bit3: PCFG3=「1」
bit2: PCFG2=「1」
bit1: PCFG1=「1」
bit0: PCFG0=「0」
ということで、B’10001110’ (8EH)をWregにロードする。
(7)ADCON1レジスタの設定
Wregの内容をADCON1レジスタに上書きする。
(8)PortB,PortDを 出力モードに設定
TRISBおよびTRISDレジスタは、CLRF(fレジスタをゼロクリアする命令)で出力設定となります。
CLRF TRISB
「TRISBレジスタをゼロクリアする。すなわち、PORTB全ポートを出力に設定する」
CLRF TRISD
「TRISDレジスタをゼロクリアする。すなわち、PORTD全ポートを出力に設定する」
なお、PortAの設定ですが、デフォルトが入力ポートになっていますので省略できます。
(9)Bank 0 へ戻す
動作モードの設定後は、Bank0に戻しておきます。Bank1へ切り替えるためには、RP0ビットを「0」にします。(RP1はデフォルトで「0」なので変える必要はない。)
BCF STATUS,RP0
「STATUS」レジスタのRP0ビットを「0」にする。すなわち、Bank0に戻す。」
(10)Fosc/32,CH0 設定,A/D on
「A/D変換の制御法」の表4.A/D変換用クロックの選択を参照してください。内部RC発振回路を使用するFrc指定の場合は、システムクロックには影響されず、2〜6μsの周波数になりますが、正確なクロック周波数ではなくなります。そこで、Fosc/2,Fosc/8,Fosc/32のどれかを選ぶことになります。システムクロックの周波数により、指定できる範囲が限られていますので注意が必要です。正しいA/D
変換のためには、必ずA/D 変換クロック(TAD)を、最小TAD
時間の1.6μs以上にする必要がありました。
20MHzのシステムクロックでは、Fosc/32を選択することになります。
また、「A/D変換の制御法」の表1.ADCON0レジスタの機能説明表を参照してください。アナログチャネル指定ビットですが、CHSx=「000」とすることで、CH0(RA0)の指定になります。
bit7: A/D変換クロック(Fosc/32)指定より ADCS1=「1」
bit6: A/D変換クロック(Fosc/32)指定より ADCS0=「0」
bit5: アナログチャネルCH0(RA0)指定より、CHS2=「0」
bit4: アナログチャネルCH0(RA0)指定より、CHS1=「0」
bit3: アナログチャネルCH0(RA0)指定より、CHS0=「0」
bit2: A/D 変換ステータスビット(初期設定時は「0」としておく)
bit1: 機能なし(とりあえず「0」とでもしておく)
bit0: A/D ON ビット(A/D機能を使うので「1」としておく)
ということで、B’10000001’ (81H)をWregにロードする。
(11)ADCON0レジスタの設定
Wregの内容をADCON0レジスタに上書きする。
(12)23.2μs 一定時間待つ
PICでは、A/D変換をするために、まずアナログ信号を一旦内部のコンデンサに蓄えます。その後、参照となる一定の電圧を加算して比較しながら計測するという逐次変換方式であるため、A/D変換を正確に行うためには、蓄積するまでの時間(アクィジション時間)が必要です。10ビットではアクィジション時間=20μsになります。
また、メインプログラムでは、A/D変換が一通り終了すると、またADSTARTへ戻り繰り返し行うようになっています。次のアクィジション開始前に、少なくとも 2TAD待つ必要があります。
すなわち、TAD=1.6μsですので、2×TAD=3.2μsとなります。よって、合計23.2μsの時間が最低限必要になります。ここで、遅延サブルーチンにジャンプします。
(13)A/D スタート
ADCON0レジスタのGOビットをセットすると、A/D変換が開始されます。
BSF ADCON0,GO
「ADCON0」レジスタのGOビットを「1」にする。すなわち、A/D変換を開始する。」
(14)A/D が完了すると GOビットが0になる
A/D変換を開始してから12TADの変換時間すなわち、12×1.6μs=19.2μs必要となります。
A/Dが完了するとGOビットが「0」となりますので、時間で確認する必要はありません。
(15)GOビットが 0 だったらループから抜ける。
GO が 1 だったら WAIT へループする
A/D変換が完了するとGOビットが「0」となり、次の命令で分岐されます。
BTFSC ADCON0,GO
「ADCON0」レジスタのGOビットが「0」だったら次の命令をスキップし、ループから抜けます。
GOTO WAIT
ただし、A/D変換が終了しない間は、「ADCON0」レジスタのGOビットが「1」のままですので、WAITラベルへ戻ってループを繰り返します。
(16)A/D データ上位(データは右詰)
MOVF ADRESH,W
「ADRESH」レジスタの内容をWregに上書きする。
データは右詰ですので、ADRESHの<1:0>ビットに上位データが入り、他のビットは「0」が入ります。
(17)PortBへ A/D 結果を出力
Wregのデータは、ADRESHレジスタの値ですので、PortBの<1:0>ビットに上位データが出力され、他のビットは「0」となります。LEDは「1」となったビットで点灯します。
MOVWF PORTB
「PORTBへWregのデータを出力します。」
(18)Bank 1 へ切替
ADRESLレジスタは、Bank1にありますので
Bankを切り替える必要があります。
(19)A/D データ下位
MOVF ADRESL,W
「ADRESL」レジスタの内容をWregに上書きする。
(注)もしBank1への切り替えを忘れて(Bank0のままで)、この命令を行った場合には、ADRESHレジスタの内容がWregに入ってしまいます。
(20)Bank 0 へ戻す
「ADRESL」レジスタの内容をWregに移したら、Bank0に戻しておきましょう。
(21)PortDへ A/D 結果を出力
Wregのデータは、ADRESLレジスタの値ですので、PortDに下位データが出力されます。LEDは「1」となったビットで点灯します。
MOVWF PORTD
「PORTDへWregのデータを出力します。」
(22)A/D 変換を繰り返す
GOTO ADSTART
「ADSTART」のラベルまで戻り、A/D変換動作を繰り返す。
【23μs遅延サブルーチンの説明】
(23)ループ回数(25H=37回)をWregにロードする。
(24)Wregの内容をfレジスタ(COUNT)に入れる
(25)サイクル数をちょうど115にするために、
1サイクル分のNOP命令(何もしないダミー)を入れた。
(26)fレジスタ(COUNT)の減算をし、結果が0なら次の命令をスキップする。
0以外である(37−1)回分は、1サイクル。
最後0になってスキップするとき2サイクル。
(27)(37−1)回分は、2サイクル
(28)サブルーチンから無条件復帰するとき、2サイクル
よって、計115サイクルになり、20MHzクロックでは、1サイクル0.2μsであることから、115サイクル×0.2μs=23μsになります。
(29)プログラム・ソースの終わり
「END」はソースの終わりを示す必ず必要な擬似命令で、アセンブラに対し、ここがソースの終わりであることを知らせる役割があります。
END
「ソースプログラムの終わりであることを示す。」
A/D変換動作の確認
図3にA/D変換の動作確認をしている様子を示します。写真の左側のボードがPICターゲットボードです。ターゲットボード左端のA/D変換入力ポート(PortA)には、コネクタケーブルが接続されており、その先はブレッドボードの回路に接続されています。中央の測定器は、MEIEX社製のテスター(M−3850D)で、A/D変換の入力電圧を測定します。
図4は、簡単にA/D変換の入力電圧を与えるため、10kΩの可変抵抗で構成されています。回路図は図1に示されています。
![]() |
![]() |
図3.A/D変換の動作を 確認している様子 |
図4.ブレッドボード上の 接続回路 |
図5に、A/D変換の各入力電圧の違いにおけるLED点灯の様子を示します。私のターゲットボードでは、PortBとPortDが隣り合って並んでいますので、10ビットの結果を右詰めに表示しています。したがって、
ADRESH<1:0>→PortB<1:0>
ADRESL<7:0>→PortD<7:0>
という配置に出力させます。
すなわち、10ビットのMSBから順に<RB1,RB0,RD7,RD6,・・・,RD0>となっています。
結果のビットですが、「1」のとき点灯し、「0」のとき消灯しています。
![]() |
![]() |
図5(a).入力電圧5V時 | 図5(b).入力電圧3V時 |
![]() |
![]() |
図5(c).入力電圧1V時 | 図5(d).入力電圧0V時 |
それでは、ターゲットボード上のLED点灯の結果から、ちゃんと動作しているかどうかチェックしていきましょう。図5(a)A/D変換入力電圧が5Vのとき、PortB<1:0>、PortD<7:0>のLEDが全点灯しています。また、図5(d)A/D変換入力電圧が0Vのとき、全消灯していますので、最大・最小電圧の入力時では一目でOKということが分かります。
次に、5V〜0V間の電圧の時はどうでしょうか。確認してみましょう。表2にA/D変換結果を求めてみました。
理論値の計算ですが、5V時の実測データ(4.97V)と、各電圧の実測データから次のように計算できます。(数字は10進表示、端数は四捨五入)
4V時は、1023×(4.00V/4.97V)=823
3V時は、1023×(3.002V/4.97V)=618
2V時は、1023×(2.000V/4.97V)=412
1V時は、1023×(1.002V/4.97V)=206
表2で、ターゲットボードの値と理論値を比べてみると、2V〜3Vあたりで最大2LSBの誤差があることが分かります。電圧換算すると、テスターでの測定誤差も含まれますが、
2/1023×4.97V=約9.7mV程度が実力のようです。
A/D変換 入力電圧 ( )内は実測値 |
出力結果 | |||||
ターゲットボード | 理論値 | |||||
2進 | 16進 | 10進 | 2進 | 16進 | 10進 | |
5V (4.97V) |
B’1111111111’ | H’3FF’ | D’1023’ | B’1111111111’ | H’3FF’ | D’1023’ |
4V (4.00V) |
B’1100111000’ | H’338’ | D’824’ | B’1100110111’ | H’337’ | D’823’ |
3V (3.002V) |
B’1001101000’ | H’268’ | D’616’ | B’1001101010’ | H’26A’ | D’618’ |
2V (2.000V) |
B’0110011010’ | H’19A’ | D’410’ | B’0110011100’ | H’19C’ | D’412’ |
1V (1.002V) |
B’0011001101’ | H’0CD’ | D’205’ | B’0011001110’ | H’0CE’ | D’206’ |
0V (0.000V) |
B’0000000000’ | H’000’ | D’000’ | B’0000000000’ | H’000’ | D’000’ |
次へは、「PICをSLEEPさせたA/D変換法」について解説します
![]() |
![]() |
![]() |